﻿using NPOI.XWPF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace ST.Common.Util.Office
{
	public class NPOIWordHelper
	{
		/// <summary>
		/// 读取word文件中的所有表格
		/// </summary>
		/// <param name="filePath"></param>
		/// <returns></returns>
		public static List<WordTable> GetAllTables(string filePath)
		{
			List<WordTable> tables = null;
			try
			{
				if (string.IsNullOrEmpty(filePath))
				{
					throw new ArgumentException("文件路径");
				}
				using (FileStream fs = File.OpenRead(filePath))
				{
					XWPFDocument doc = new XWPFDocument(fs);
					if (doc.Tables != null)
					{
						tables = new List<WordTable>();
						int i = 0;
						int j = 0;
						int k = 0;
						foreach (var table in doc.Tables)
						{
							var tbl = new WordTable();
							tbl.Index = i++;
							tbl.Rows = new List<WordTableRow>();
							tables.Add(tbl);
							foreach (var row in table.Rows)
							{
								var r = new WordTableRow();
								r.Index = j++;
								r.Cells = new List<WordTableCell>();
								tbl.Rows.Add(r);
								var cells = row.GetTableCells();
								foreach (var cell in cells)
								{
									var c = new WordTableCell();
									c.Index = k++;
									r.Cells.Add(c);
									StringBuilder buf = new StringBuilder();
									foreach (var para in cell.Paragraphs)
									{
										buf.AppendLine(para.ParagraphText);
									}
									c.Content = buf.ToString().TrimEnd(new char[] { '\r', '\n' });
								}
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return tables;
		}

		/// <summary>
		/// 读取word文件中的包含指定列的第一个表格
		/// </summary>
		/// <param name="filePath"></param>
		/// <returns></returns>
		public static WordTable GetSingleTable(string filePath, string tableHeaderColumnKeyword)
		{
			WordTable result = null;
			try
			{
				if (string.IsNullOrEmpty(filePath))
				{
					throw new ArgumentException("文件路径不能为空");
				}
				if (string.IsNullOrEmpty(tableHeaderColumnKeyword))
				{
					throw new ArgumentException("表头关键字不能为空");
				}
				XWPFTable table = null;
				using (FileStream fs = File.OpenRead(filePath))
				{
					//HWPFDocument doc = new HWPFDocument();
					XWPFDocument doc = new XWPFDocument(fs);
					if (doc.Tables != null)
					{
						foreach (var tbl in doc.Tables)
						{
							if (tbl.Rows == null || tbl.Rows.Count == 0)
							{
								continue;
							}
							var tblCells = tbl.Rows[0].GetTableCells();//只获取首行标题
							foreach (var cell in tblCells)
							{
								StringBuilder buf = new StringBuilder();
								foreach (var para in cell.Paragraphs)
								{
									buf.Append(para.ParagraphText == null ? "" : para.ParagraphText.TrimEnd(new char[] { '\r', '\n' }));
								}
								if (buf.ToString() == tableHeaderColumnKeyword)
								{
									table = tbl;
									break;//跳出表格列循环
								}
							}
							if (table != null)
							{
								break;//跳出word中表格循环
							}
						}
					}
					if (table != null)
					{
						result = new WordTable();
						result.Rows = new List<WordTableRow>();
						for (int i = 0; i < table.Rows.Count; i++)
						{
							var r = new WordTableRow();
							r.Cells = new List<WordTableCell>();
							result.Rows.Add(r);
							var cells = table.Rows[i].GetTableCells();
							foreach (var cell in cells)
							{
								var c = new WordTableCell();
								c.Index = r.Cells.Count;
								r.Cells.Add(c);
								StringBuilder buf = new StringBuilder();
								foreach (var para in cell.Paragraphs)
								{
									buf.Append(para.ParagraphText == null ? "" : para.ParagraphText.TrimEnd(new char[] { '\r', '\n' }));
								}
								c.Content = buf.ToString();
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
			return result;
		}

		public static byte[] GenReplaceDoc<T>(string filePath, T obj)
		{
			Dictionary<string, string> bmValues = new Dictionary<string, string>();
			Type entityType = typeof(T);
			PropertyInfo[] properties = entityType.GetProperties();
			foreach (var p in properties)
			{
				object value = p.GetValue(obj);
				var key = "${" + p.Name + "}";
				if (!bmValues.ContainsKey(key))
				{
					bmValues.Add(key, Convert.ToString(value));
				}
			}
			return GenReplaceDoc(filePath, bmValues);
		}

		public static byte[] GenReplaceDoc(string filePath, Dictionary<string, string> bmValues)
		{
			try
			{
				if (string.IsNullOrEmpty(filePath))
				{
					throw new ArgumentException("文件路径不能为空");
				}
				//if (bmValues == null || bmValues.Count == 0)
				//{
				//	throw new ArgumentException("书签替换值不能为空");
				//}
				if (!File.Exists(filePath))
				{
					throw new ArgumentException("模板文件不存在");
				}
				using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
				{
					XWPFDocument doc = null;
					if (filePath.EndsWith(".docx", StringComparison.OrdinalIgnoreCase))
					{
						doc = new XWPFDocument(fs);
					}
					else
					{
						throw new BuzException("只支持docx格式模板");
					}
					Regex regex = new Regex("\\$\\{[a-zA-Z]+[a-zA-Z0-9_]*\\}", RegexOptions.Compiled);
					foreach (var p in doc.Paragraphs)//遍历段落
					{
						ReplaceParagraph(p, bmValues, regex);
					}
					foreach (var tbl in doc.Tables)//遍历表格
					{
						foreach (var row in tbl.Rows)
						{
							var cells = row.GetTableCells();
							foreach (var cell in cells)
							{
								foreach (var p in cell.Paragraphs)
								{
									ReplaceParagraph(p, bmValues, regex);
								}
							}
						}
					}
					byte[] bytes = null;
					using (MemoryStream ms = new MemoryStream())
					{
						doc.Write(ms);
						bytes = ms.ToArray();
					}
					return bytes;
				}
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		public static void ReplaceParagraph(XWPFParagraph paragraph, Dictionary<string, string> bmValues, Regex regex)
		{
			var mc = regex.Matches(paragraph.Text);
			if (mc.Count == 0)
			{
				return;
			}
			int i;
			for (i = 0; i < mc.Count; i++)
			{
				if (bmValues.ContainsKey(mc[i].Value))
				{
					paragraph.ReplaceText(mc[i].Value, bmValues[mc[i].Value]);
				}
				else
				{
					paragraph.ReplaceText(mc[i].Value, "");
				}
			}
		}

		private void RemoveAllRuns(XWPFParagraph paragraph)
		{
			int size = paragraph.Runs.Count;
			for (int i = 0; i < size; i++)
			{
				paragraph.RemoveRun(0);
			}
		}

		//public static void ReplaceParagraph(XWPFParagraph p, Dictionary<string, string> values, Regex regex)
		//{
		//	//String pText = p.ParagraphText; // complete paragraph as string
		//	//var m = regex.Matches(pText);
		//	//if (m.Count <= 0)
		//	//{
		//	//	return;
		//	//}
		//	//Dictionary<int, XWPFRun> posRuns = GetPosToRuns(p);

		//	//for (int i = 0; i < m.Count; i++)
		//	//{ // for all patterns in the paragraph
		//	//	String g = m.group(1);  // extract key start and end pos
		//	//	int s = m.start(1);
		//	//	int e = m.end(1);
		//	//	String key = g;
		//	//	String x = data.get(key);
		//	//	if (x == null)
		//	//		x = "";
		//	//	Dictionary<int, XWPFRun> range = posRuns.subMap(s - 2, true, e + 1, true); // get runs which contain the pattern
		//	//	bool found1 = false; // found $
		//	//	bool found2 = false; // found {
		//	//	bool found3 = false; // found }
		//	//	XWPFRun prevRun = null; // previous run handled in the loop
		//	//	XWPFRun found2Run = null; // run in which { was found
		//	//	int found2Pos = -1; // pos of { within above run
		//	//	for (XWPFRun r : range.values())
		//	//	{
		//	//		if (r == prevRun)
		//	//			continue; // this run has already been handled
		//	//		if (found3)
		//	//			break; // done working on current key pattern
		//	//		prevRun = r;
		//	//		for (int k = 0; ; k++)
		//	//		{ // iterate over texts of run r
		//	//			if (found3)
		//	//				break;
		//	//			String txt = null;
		//	//			try
		//	//			{
		//	//				txt = r.getText(k); // note: should return null, but throws exception if the text does not exist
		//	//			}
		//	//			catch (Exception ex)
		//	//			{

		//	//			}
		//	//			if (txt == null)
		//	//				break; // no more texts in the run, exit loop
		//	//			if (txt.contains("$") && !found1)
		//	//			{  // found $, replace it with value from data map
		//	//				txt = txt.replaceFirst("\\$", x);
		//	//				found1 = true;
		//	//			}
		//	//			if (txt.contains("{") && !found2 && found1)
		//	//			{
		//	//				found2Run = r; // found { replace it with empty string and remember location
		//	//				found2Pos = txt.indexOf('{');
		//	//				txt = txt.replaceFirst("\\{", "");
		//	//				found2 = true;
		//	//			}
		//	//			if (found1 && found2 && !found3)
		//	//			{ // find } and set all chars between { and } to blank
		//	//				if (txt.contains("}"))
		//	//				{
		//	//					if (r == found2Run)
		//	//					{ // complete pattern was within a single run
		//	//						txt = txt.substring(0, found2Pos) + txt.substring(txt.indexOf('}'));
		//	//					}
		//	//					else // pattern spread across multiple runs
		//	//						txt = txt.substring(txt.indexOf('}'));
		//	//				}
		//	//				else if (r == found2Run) // same run as { but no }, remove all text starting at {
		//	//					txt = txt.substring(0, found2Pos);
		//	//				else
		//	//					txt = ""; // run between { and }, set text to blank
		//	//			}
		//	//			if (txt.contains("}") && !found3)
		//	//			{
		//	//				txt = txt.replaceFirst("\\}", "");
		//	//				found3 = true;
		//	//			}
		//	//			r.setText(txt, k);
		//	//		}
		//	//	}
		//	//}
		//}

		private static Dictionary<int, XWPFRun> GetPosToRuns(XWPFParagraph paragraph)
		{
			int pos = 0;
			Dictionary<int, XWPFRun> map = new Dictionary<int, XWPFRun>();
			foreach (var run in paragraph.Runs)
			{
				String runText = run.Text;
				if (!string.IsNullOrEmpty(runText))
				{
					for (int i = 0; i < runText.Length; i++)
					{
						map.Add(pos + i, run);
					}
					pos += runText.Length;
				}

			}
			return map;
		}
	}
}
